From b882e9ddd5f8e09d3d26dda58af0d5f5784bef24 Mon Sep 17 00:00:00 2001 From: "mwilli2@equilibrium.research.intel-research.net" Date: Mon, 23 Feb 2004 17:56:51 +0000 Subject: [PATCH] bitkeeper revision 1.741 (403a3ee3r3VblO5vtoFGItOp9rvEpg) Add Xen trace buffer and supporting tools. --- .rootkeys | 6 + tools/Makefile | 4 + tools/xentrace/Makefile | 32 ++ tools/xentrace/xentrace.c | 428 ++++++++++++++++++++++++++ tools/xentrace/xentrace_cpusplit.py | 59 ++++ tools/xentrace/xentrace_format.py | 96 ++++++ xen/arch/i386/setup.c | 5 + xen/common/dom0_ops.c | 11 + xen/common/trace.c | 103 +++++++ xen/include/hypervisor-ifs/dom0_ops.h | 11 +- xen/include/xeno/config.h | 1 + xen/include/xeno/trace.h | 179 +++++++++++ 12 files changed, 934 insertions(+), 1 deletion(-) create mode 100644 tools/xentrace/Makefile create mode 100644 tools/xentrace/xentrace.c create mode 100644 tools/xentrace/xentrace_cpusplit.py create mode 100644 tools/xentrace/xentrace_format.py create mode 100644 xen/common/trace.c create mode 100644 xen/include/xeno/trace.h diff --git a/.rootkeys b/.rootkeys index 15ed120cc5..cd0b234a42 100644 --- a/.rootkeys +++ b/.rootkeys @@ -86,6 +86,10 @@ 3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c 3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py 3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py +403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile +403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c +403a3edb0lzD0Fojc-NYNoXr3SYrnA tools/xentrace/xentrace_cpusplit.py +403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format.py 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk @@ -150,6 +154,7 @@ 3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c 3e7f358awXBC3Vw-wFRwPw18qL1khg xen/common/string.c 3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen/common/timer.c +403a3edbejm33XLTGMuinKEwQBrOIg xen/common/trace.c 3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen/common/vsprintf.c 3ddb79c0ppNeJtjC4va8j41ADCnchA xen/drivers/Makefile 3ddb79beWzgPS8ozf2BL2g3ZkiWhhQ xen/drivers/block/Makefile @@ -491,6 +496,7 @@ 3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen/include/xeno/timer.h 3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h 3ddb79c2e2C14HkndNEJlYwXaPrF5A xen/include/xeno/tqueue.h +403a3edbG9K5uZjuY19_LORbQGmFbA xen/include/xeno/trace.h 3ddb79c1-kVvF8cVa0k3ZHDdBMj01Q xen/include/xeno/types.h 3f055a3dwldYR102YcSuBaxIf9t3Jw xen/include/xeno/vbd.h 3e8827bdaqPeZAWGVOwswgY9bWSx4g xen/include/xeno/version.h diff --git a/tools/Makefile b/tools/Makefile index cf8b9fc0fa..4717987d6c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -22,18 +22,21 @@ all: $(MAKE) -C xc $(MAKE) -C misc $(MAKE) -C examples + $(MAKE) -C xentrace install: all $(MAKE) -C balloon install $(MAKE) -C xc install $(MAKE) -C misc install $(MAKE) -C examples install + $(MAKE) -C xentrace install dist: all $(MAKE) -C balloon dist $(MAKE) -C xc dist $(MAKE) -C misc dist $(MAKE) -C examples dist + $(MAKE) -C xentrace dist clean: $(MAKE) -C balloon clean @@ -41,4 +44,5 @@ clean: $(MAKE) -C misc clean $(MAKE) -C examples clean $(MAKE) -C nsplitd clean + $(MAKE) -C xentrace clean diff --git a/tools/xentrace/Makefile b/tools/xentrace/Makefile new file mode 100644 index 0000000000..52574e8e04 --- /dev/null +++ b/tools/xentrace/Makefile @@ -0,0 +1,32 @@ + +CC = gcc +CFLAGS = -Wall -O3 +CFLAGS += -I../../xen/include -I../../xenolinux-sparse/include + +HDRS = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +BIN = xentrace +SCRIPTS = $(wildcard *.py) + +all: $(BIN) + +install: all + mkdir -p /usr/bin + cp $(BIN) /usr/bin + cp $(SCRIPTS) /usr/bin + chmod 755 /usr/bin/$(BIN) + for i in $(SCRIPTS); do chmod 755 /usr/bin/$$i; done + +dist: all + mkdir -p ../../../install/bin + cp $(BIN) ../../../install/bin + chmod 755 ../../../install/bin/$(BIN) + cp $(SCRIPTS) ../../../install/bin + for i in $(SCRIPTS); do chmod 755 ../../../install/bin/$i; done + +clean: + $(RM) *.a *.so *.o *.rpm $(BIN) + +%: %.c $(HDRS) Makefile + $(CC) $(CFLAGS) -o $@ $< -lxc diff --git a/tools/xentrace/xentrace.c b/tools/xentrace/xentrace.c new file mode 100644 index 0000000000..9da21d6a81 --- /dev/null +++ b/tools/xentrace/xentrace.c @@ -0,0 +1,428 @@ +/****************************************************************************** + * + * tools/xentrace/xentrace.c + * + * Tool for collecting trace buffer data from Xen. + * + * Copyright (C) 2004 by Intel Research Cambridge + * + * Author: Mark Williamson, mark.a.williamson@intel.com + * Date: February 2004 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../xc/lib/xc_private.h" + +#define TRACE_BUFFER /* need to define this for trace.h */ +#include + +extern FILE *stdout; + +/***** Compile time configuration of defaults ********************************/ + +#define NUM_CPUS 1 /* XXX this ought to be removed and replaced with something + * cleverer to dynamically query the machine - I'll use a + * dom0 op once I've implemented it! :-) */ + +/* when we've got more records than this waiting, we log it to the output */ +#define NEW_DATA_THRESH 1 + +/* sleep for this long (milliseconds) between checking the trace buffers */ +#define POLL_SLEEP_MILLIS 100 + +/***** The code **************************************************************/ + +typedef struct settings_st { + char *outfile; + unsigned int num_cpus; + struct timespec poll_sleep; + unsigned long new_data_thresh; +} settings_t; + +settings_t opts; + +int interrupted = 0; /* gets set if we get a SIGHUP */ + +void close_handler(int signal) +{ + interrupted = 1; + fprintf(stderr,"Received signal %d, now exiting\n", signal); +} + +/** + * millis_to_timespec - convert a time in milliseconds to a struct timespec + * @millis: time interval in milliseconds + */ +struct timespec millis_to_timespec(unsigned long millis) +{ + struct timespec spec; + + spec.tv_sec = millis / 1000; + spec.tv_nsec = (millis % 1000) * 1000; + + return spec; +} + + +/** + * print_rec - plain print an event given a pointer to its start + * @cpu: CPU the data came from + * @data: pointer to the start of the event data + * @out: file stream to print out to + * + * Takes a pointer to a record and prints out the data. + */ +void print_rec(unsigned int cpu, struct t_rec *rec, FILE *out) +{ + fprintf(out, "%u %llu %lu %lu %lu %lu %lu %lu\n", + cpu, rec->cycles, rec->event, rec->d1, rec->d2, + rec->d3, rec->d4, rec->d5); +} + + +/** + * get_tbuf_ptrs - get pointer to trace buffers + * + * Does a dom0 op to fetch a "pointer" to the trace buffers. The pointer can't + * be dereferenced immediately, since it is a physical address of memory in Xen + * space - they are used in this program to mmap the right area from /dev/mem. + */ +struct t_buf *get_tbuf_ptrs() +{ + int ret; + dom0_op_t op; /* dom0 op we'll build */ + int xc_handle = xc_interface_open(); /* for accessing control interface */ + + op.cmd = DOM0_GETTBUFS; + op.interface_version = DOM0_INTERFACE_VERSION; + + ret = do_dom0_op(xc_handle, &op); + + xc_interface_close(xc_handle); + + if(ret) { + PERROR("Failure to get trace buffer pointer from Xen"); + exit(EXIT_FAILURE); + } + + return op.u.gettbufs.phys_addr; +} + +/** + * map_tbufs - memory map Xen trace buffers into user space + * @tbufs: physical address of the trace buffers + * + * Given the physical address of the Xen trace buffers, maps them into process + * address space by memory mapping /dev/mem. Returns a pointer to the location + * the buffers have been mapped to. + */ +struct t_buf *map_tbufs(struct t_buf *tbufs_phys) +{ + int dm_fd; /* file descriptor for /dev/mem */ + struct t_buf *tbufs_mapped; + + dm_fd = open("/dev/mem", O_RDONLY); + + if(dm_fd < 0) { + PERROR("Open /dev/mem when mapping trace buffers\n"); + exit(EXIT_FAILURE); + } + + tbufs_mapped = (struct t_buf *)mmap(NULL, opts.num_cpus * TB_SIZE, + PROT_READ, MAP_SHARED, + dm_fd, (off_t)tbufs_phys); + + close(dm_fd); + + if(tbufs_mapped == MAP_FAILED) { + PERROR("Failed to mmap trace buffers"); + exit(EXIT_FAILURE); + } + + return tbufs_mapped; +} + + +/** + * init_bufs_ptrs - initialises an array of pointers to the trace buffers + * @bufs_mapped: the userspace address where the trace buffers are mapped + * + * Initialises an array of pointers to individual trace buffers within the + * mapped region containing all trace buffers. + */ +struct t_buf **init_bufs_ptrs(void *bufs_mapped) +{ + int i; + struct t_buf **user_ptrs; + + user_ptrs = (struct t_buf **)calloc(opts.num_cpus, sizeof(struct t_buf *)); + + if(!user_ptrs) { + PERROR( "Failed to allocate memory for buffer pointers\n"); + exit(EXIT_FAILURE); + } + + /* initialise pointers to the trace buffers - given the size of a trace + * buffer and the value of bufs_maped, we can easily calculate these */ + for(i = 0; idata + - (unsigned long)tbufs_phys + + (unsigned long)tbufs_mapped + ); + } + + return data; +} + +/** + * init_tail_idxs - initialise an array of tail indexes + * @bufs: array of pointers to trace buffer metadata in struct t_buf's + * + * The tail indexes indicate where we're read to so far in the data array of a + * trace buffer. Each entry in this table corresponds to the tail index for a + * particular trace buffer. + */ +int *init_tail_idxs(struct t_buf **bufs) +{ + int i; + int *tails = calloc(opts.num_cpus, sizeof(unsigned int)); + + if(!tails) { + PERROR("Failed to allocate memory for tail pointers\n"); + exit(EXIT_FAILURE); + } + + for(i = 0; ihead; + + return tails; +} + + +/** + * monitor_tbufs - monitor the contents of tbufs and output to a file + * @logfile: the FILE * representing the file to log to + */ +int monitor_tbufs(FILE *logfile) +{ + int i, j; + void *tbufs_mapped; /* pointer to where the tbufs are mapped */ + struct t_buf **meta; /* pointers to the trace buffer metadata */ + struct t_rec **data; /* pointers to the trace buffer data areas + * where they are mapped into user space. */ + int *tails; /* store tail indexes for the trace buffers */ + struct t_buf *tbufs_phys; /* physical address of the tbufs */ + + /* setup access to trace buffers */ + tbufs_phys = get_tbuf_ptrs(); + tbufs_mapped = map_tbufs(tbufs_phys); + + /* build arrays of convenience ptrs */ + meta = init_bufs_ptrs (tbufs_mapped); + data = init_rec_ptrs (tbufs_phys, tbufs_mapped, meta); + tails = init_tail_idxs (meta); + + /* now, scan buffers for events */ + while(!interrupted) { + for(i = 0; i < opts.num_cpus; i++) { + signed long newdata = meta[i]->head - tails[i]; + signed long prewrap = newdata; + + /* correct newdata and prewrap in case of a pointer wrap */ + if(newdata < 0) { + newdata += meta[i]->size; + prewrap = meta[i]->size - tails[i]; + } + + if(newdata >= opts.new_data_thresh) { + /* output pre-wrap data */ + for(j = 0; j < prewrap; j++) + print_rec(i, data[i] + tails[i] + j, logfile); + + /* output post-wrap data, if any */ + for(j = 0; j < (newdata - prewrap); j++) + print_rec(i, data[i] + j, logfile); + + tails[i] += newdata; + if(tails[i] >= meta[i]->size) tails[i] = 0; + } + } + nanosleep(&opts.poll_sleep, NULL); + } + + /* cleanup */ + free(meta); + free(data); + free(tails); + /* don't need to munmap - cleanup is automatic */ + fclose(logfile); + + return 0; +} + + +/****************************************************************************** + * Various declarations / definitions GNU argp needs to do its work + *****************************************************************************/ + + +/* command parser for GNU argp - see GNU docs for more info */ +error_t cmd_parser(int key, char *arg, struct argp_state *state) +{ + settings_t *setup = (settings_t *)state->input; + + switch(key) + { + case 't': /* set new records threshold for logging */ + { + char *inval; + setup->new_data_thresh = strtol(arg, &inval, 0); + if(inval == arg) argp_usage(state); + } + + break; + + case 's': /* set sleep time (given in milliseconds) */ + { + char *inval; + setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0)); + if(inval == arg) argp_usage(state); + } + break; + + case 'n': /* set number of CPU trace buffers to map */ + { + char *inval; + setup->num_cpus = strtol(arg, &inval, 0); + if(inval == arg) argp_usage(state); + } + break; + + case ARGP_KEY_ARG: + if(state->arg_num == 0) + setup->outfile = arg; + else + argp_usage(state); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +#define xstr(x) str(x) +#define str(x) #x + +const struct argp_option cmd_opts[] = +{ + { .name = "log-thresh", .key='t', .arg="l", + .doc = + "Set number, l, of new records required to trigger a write to output " + "(default " xstr(NEW_DATA_THRESH) ")." }, + + { .name = "poll-sleep", .key='s', .arg="p", + .doc = + "Set sleep time, p, in milliseconds between polling the trace buffer " + "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." }, + + { .name = "num-cpus", .key = 'n', .arg="i", + .doc = + "Set number, i, of CPU trace buffers to map. This should not exceed " + "the number of CPUs in the system (default " xstr(NUM_CPUS) ")." }, + + {0} +}; + +const struct argp parser_def = +{ + .options = cmd_opts, + .parser = cmd_parser, + .args_doc = "[output file]", + .doc = + "Tool to capure Xen trace buffer data" + "\v" + "This tool is used to capture trace buffer data from Xen. The data is " + "output as space-separated decimal numbers, represented in ASCII, in " + "the following order:\n\n" + " CPU TSC EVENT DATA1 DATA2 DATA3 DATA4 DATA5\n" +}; + + +const char *argp_program_version = "xentrace v1.0"; +const char *argp_program_bug_address = ""; + + +int main(int argc, char **argv) +{ + int ret; + FILE *logfile = stdout; + + const struct sigaction act = { .sa_handler = close_handler }; + + opts.outfile = 0; + opts.num_cpus = 1; + opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS); + opts.new_data_thresh = NEW_DATA_THRESH; + + argp_parse(&parser_def, argc, argv, 0, 0, &opts); + + if(opts.outfile) { + logfile = fopen(opts.outfile, "w"); + } + + /* ensure that if we get a signal, we'll do cleanup, then exit */ + sigaction(SIGHUP, &act, 0); + sigaction(SIGTERM, &act, 0); + sigaction(SIGINT, &act, 0); + + ret = monitor_tbufs(logfile); + + return ret; +} diff --git a/tools/xentrace/xentrace_cpusplit.py b/tools/xentrace/xentrace_cpusplit.py new file mode 100644 index 0000000000..9847b79b38 --- /dev/null +++ b/tools/xentrace/xentrace_cpusplit.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# by Mark Williamson, (C) 2004 Intel Research Cambridge + +# Program for separating trace buffer data into per-cpu log files. + +import re, sys, signal + +def usage(): + print >> sys.stderr, \ + "Usage: " + sys.argv[0] + """ base-name + Separates ASCII trace buffer data on stdin into per-CPU trace + files. The trace files are named by appending the CPU number + to the base name supplied on the command line. + + Depending on your system and the volume of trace buffer data, + this script may not be able to keep up with the output of xentrace + if it is piped directly. In these circumstances you should have + xentrace output to a file for processing off-line. + """ + sys.exit(1) + +def sighand(x,y): + global interrupted + interrupted = 1 + +signal.signal(signal.SIGTERM, sighand) +signal.signal(signal.SIGHUP, sighand) +signal.signal(signal.SIGINT, sighand) + +r = re.compile("(\d) .*") + +if len(sys.argv) < 2: + usage() +else: + base_name = sys.argv[1] + +files = {} +interrupted = 0 + +while not interrupted: + try: + line = sys.stdin.readline() + if not line: break + + m = r.match(line) + + if not m: print >> sys.stderr, "Invalid input line." + + cpu = m.group(1) + + if not files.has_key(base_name + str(cpu)): + files[base_name + str(cpu)] = open(base_name + str(cpu), "w") + + print >> files[base_name + str(cpu)], line, + + except IOError: sys.exit() + +# files closed automatically diff --git a/tools/xentrace/xentrace_format.py b/tools/xentrace/xentrace_format.py new file mode 100644 index 0000000000..b8f8d018ce --- /dev/null +++ b/tools/xentrace/xentrace_format.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# by Mark Williamson, (C) 2004 Intel Research Cambridge + +# Program for reformatting trace buffer output according to user-supplied rules + +import re, sys, string, signal + +def usage(): + print >> sys.stderr, \ + "Usage: " + sys.argv[0] + """ defs-file + Parses trace data in ASCII format and reformats it according to the + rules in a file of definitions. The rules in this file should have + the format ({ and } show grouping and are not part of the syntax): + + {event_id}{whitespace}{text format string} + + The textual format string may include the format specifiers: + %(cpu)s, %(tsc), %(event)s, %(1)s, %(2)s, %(3)s, %(4)s, %(5)s + + Which correspond to the CPU number, event ID, timestamp counter and + the 5 data fields from the trace record. There should be one such + rule for each type of event. + + Depending on your system and the volume of trace buffer data, + this script may not be able to keep up with the output of xentrace + if it is piped directly. In these circumstances you should have + xentrace output to a file for processing off-line. + """ + sys.exit(1) + +def read_defs(defs_file): + defs = {} + + fd = open(defs_file) + + reg = re.compile('(\d+)\s+(\S.*)') + + while True: + line = fd.readline() + if not line: + break + + m = reg.match(line) + + if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1) + + defs[m.group(1)] = m.group(2) + + return defs + +def sighand(x,y): + global interrupted + interrupted = 1 + +##### Main code + +if len(sys.argv) < 2: + usage() + +signal.signal(signal.SIGTERM, sighand) +signal.signal(signal.SIGHUP, sighand) +signal.signal(signal.SIGINT, sighand) + +interrupted = 0 + +defs = read_defs(sys.argv[1]) + +reg = re.compile('(\d+) (\d+) (\d+) (.*)') + +while not interrupted: + try: + line = sys.stdin.readline() + if not line: + break + + m = reg.match(line) + + if not m: print >> sys.stderr, "Invalid input line." + + s = string.split(m.group(4)) + + args = {'cpu' : m.group(1), + 'tsc' : m.group(2), + 'event' : m.group(3) } + + i = 0 + for item in s: + args[str(i)] = item + i += 1 + + if defs.has_key(m.group(3)): print defs[m.group(3)] % args + # silently skip lines we don't have a format for - a 'complain' option + # should be added if needed + + except IOError: sys.exit() diff --git a/xen/arch/i386/setup.c b/xen/arch/i386/setup.c index ac2df96139..2e5c315530 100644 --- a/xen/arch/i386/setup.c +++ b/xen/arch/i386/setup.c @@ -14,6 +14,7 @@ #include #include #include +#include struct cpuinfo_x86 boot_cpu_data = { 0 }; /* Lots of nice things, since we only target PPro+. */ @@ -449,4 +450,8 @@ void __init start_of_day(void) #endif watchdog_on = 1; + +#ifdef TRACE_BUFFER + init_trace_bufs(); /* initialise trace buffers */ +#endif } diff --git a/xen/common/dom0_ops.c b/xen/common/dom0_ops.c index 79f4ecc285..c225dffd3f 100644 --- a/xen/common/dom0_ops.c +++ b/xen/common/dom0_ops.c @@ -16,6 +16,7 @@ #include #include #include +#include extern unsigned int alloc_new_dom_mem(struct task_struct *, unsigned int); @@ -431,6 +432,16 @@ long do_dom0_op(dom0_op_t *u_dom0_op) ret = 0; } break; + +#ifdef TRACE_BUFFER + case DOM0_GETTBUFS: + { + op->u.gettbufs.phys_addr = get_tb_ptr(); + copy_to_user(u_dom0_op, op, sizeof(*op)); + ret = 0; + } + break; +#endif case DOM0_READCONSOLE: { diff --git a/xen/common/trace.c b/xen/common/trace.c new file mode 100644 index 0000000000..6e741b7f34 --- /dev/null +++ b/xen/common/trace.c @@ -0,0 +1,103 @@ +/****************************************************************************** + * + * common/trace.c + * + * Xen Trace Buffer + * + * Copyright (C) 2004 by Intel Research Cambridge + * + * Author: Mark Williamson, mark.a.williamson@intel.com + * Date: January 2004 + * + * The trace buffer code is designed to allow debugging traces of Xen to be + * generated on UP / SMP machines. Each trace entry is timestamped so that + * it's possible to reconstruct a chronological record of trace events. + * + * See also include/xeno/trace.h and the dom0 op in + * include/hypervisor-ifs/dom0_ops.h + * + *****************************************************************************/ + +#include + +#ifdef TRACE_BUFFER /* don't compile this stuff in unless explicitly enabled */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Pointers to the meta-data objects for all system trace buffers */ +struct t_buf *t_bufs[NR_CPUS]; + +/* a flag recording whether initialisation has been done */ +atomic_t tb_init_done = ATOMIC_INIT(0); + + +/** + * init_trace_bufs - performs initialisation of the per-cpu trace buffers. + * + * This function is called at start of day in order to initialise the per-cpu + * trace buffers. The trace buffers are then available for debugging use, via + * the %TRACE_xD macros exported in . + */ +void init_trace_bufs() +{ + int i; + void *d; /* trace buffer area pointer */ + + d = kmalloc(smp_num_cpus * TB_SIZE, GFP_KERNEL); + + if( d == NULL ) { + printk("Xen trace buffers: memory allocation failed\n"); + return; + } + + for(i = 0; i < smp_num_cpus; i++) { + struct t_buf *buf = t_bufs[i] + = (struct t_buf *)( (unsigned int)d + TB_SIZE * i ); + + /* for use in Xen */ + buf->vdata = (struct t_rec *) + ( (unsigned int)buf + sizeof(struct t_buf) ); + buf->head_ptr = buf->vdata; + spin_lock_init(&buf->lock); + + /* for use in user space */ + buf->data = (struct t_rec *)__pa(buf->vdata); + buf->head = 0; + + /* for use in both */ + buf->size = (TB_SIZE - sizeof(struct t_buf)) / sizeof(struct t_rec); + } + + printk("Xen trace buffers: initialised\n"); + + wmb(); /* above must be visible before tb_init_done flag set */ + + atomic_set(&tb_init_done, 1); +} + + + +/** + * get_tb_ptr - return physical address of the trace buffers. + * + * Called by the %DOM0_GETTBUFS dom0 op to fetch the physical address of the + * trace buffers. + */ +struct t_buf *get_tb_ptr() +{ + /* a physical address (user space maps this using /dev/mem) */ + return (struct t_buf *)__pa(t_bufs[0]); +} + +#endif /* #ifdef TRACE_BUFFER */ diff --git a/xen/include/hypervisor-ifs/dom0_ops.h b/xen/include/hypervisor-ifs/dom0_ops.h index 4562aa1c49..aeb1f51917 100644 --- a/xen/include/hypervisor-ifs/dom0_ops.h +++ b/xen/include/hypervisor-ifs/dom0_ops.h @@ -17,7 +17,7 @@ * This makes sure that old versions of dom0 tools will stop working in a * well-defined way (rather than crashing the machine, for instance). */ -#define DOM0_INTERFACE_VERSION 0xAAAA0005 +#define DOM0_INTERFACE_VERSION 0xAAAA0006 /* @@ -210,6 +210,14 @@ typedef struct dom0_pincpudomain_st int cpu; /* -1 implies unpin */ } dom0_pincpudomain_t; +/* Get trace buffers physical base pointer */ +#define DOM0_GETTBUFS 21 +typedef struct dom0_gettbufs_st +{ + /* OUT variable - location of the trace buffers */ + struct t_buf *phys_addr; +} dom0_gettbufs_t; + typedef struct dom0_op_st { unsigned long cmd; @@ -232,6 +240,7 @@ typedef struct dom0_op_st dom0_settime_t settime; dom0_readconsole_t readconsole; dom0_pincpudomain_t pincpudomain; + dom0_gettbufs_t gettbufs; } u; } dom0_op_t; diff --git a/xen/include/xeno/config.h b/xen/include/xeno/config.h index 35cdd22ee4..e8dffa1a2a 100644 --- a/xen/include/xeno/config.h +++ b/xen/include/xeno/config.h @@ -149,6 +149,7 @@ #define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \ __FILE__ , __LINE__ , ## _a ) #define STACK_GUARD +#define TRACE_BUFFER #else #define DPRINTK(_f, _a...) ((void)0) #endif diff --git a/xen/include/xeno/trace.h b/xen/include/xeno/trace.h new file mode 100644 index 0000000000..c6a1a840b6 --- /dev/null +++ b/xen/include/xeno/trace.h @@ -0,0 +1,179 @@ +/****************************************************************************** + * + * include/xeno/trace.h + * + * Xen Trace Buffer + * + * Copyright (C) 2003 by Intel Research Cambridge + * + * Author: Mark Williamson, mark.a.williamson@intel.com + * Date: January 2004 + * + * The trace buffer code is designed to allow debugging traces of Xen to be + * generated on UP / SMP machines. Each trace entry is timestamped so that + * it's possible to reconstruct a chronological record of trace events. + * + * Access to the trace buffers is via a dom0 hypervisor op and analysis of + * trace buffer contents can then be performed using a userland tool. + * + * See also common/trace.c and the dom0 op in include/hypervisor-ifs/dom0_ops.h + * + *****************************************************************************/ + +#ifdef TRACE_BUFFER + +#ifndef __XENO_TRACE_H__ +#define __XENO_TRACE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include + +#endif /* #ifdef __KERNEL__ */ + +/****************************************************************************** + * Data structure declarations + *****************************************************************************/ + +/* This structure represents a single trace buffer record. */ +struct t_rec { + u64 cycles; /* 64 bit cycle counter timestamp */ + u32 event; /* 32 bit event ID */ + u32 d1, d2, d3, d4, d5; /* event data items */ +}; + +/* This structure contains the metadata for a single trace buffer. The head + * field, indexes into an array of struct t_rec's. + */ +struct t_buf { + struct t_rec *data; /* pointer to data area. physical address + * for convenience in user space code */ + + unsigned int size; /* size of the data area, in t_recs */ + unsigned int head; /* array index of the most recent record */ + +#ifdef __KERNEL__ + struct t_rec *head_ptr; /* pointer to the head record */ + struct t_rec *vdata; /* virtual address pointer to data, + * for use in Xen */ + spinlock_t lock; /* ensure mutually exlusive access (for inserts) */ +#endif /* #ifdef __KERNEL__ */ + + /* never add anything here - the kernel stuff must be the last elements */ +}; + +/****************************************************************************** + * Functions + *****************************************************************************/ + +#ifdef __KERNEL__ + +/* Used to initialise trace buffer functionality */ +void init_trace_bufs(); + +/* used to retrieve the physical address of the trace buffers */ +struct t_buf *get_tb_ptr(); + +/** + * trace - Enters a trace tuple into the trace buffer for the current CPU. + * @event: the event type being logged + * @d1...d5: the data items for the event being logged + * + * Logs a trace record into the appropriate buffer. Returns nonzero on + * failure, otherwise 0. Failure occurs only if the trace buffers are not yet + * initialised. + */ +static inline int trace(u32 event, u32 d1, u32 d2, u32 d3, u32 d4, u32 d5) +{ + extern struct t_buf *t_bufs[]; /* global array of pointers to bufs */ + extern atomic_t tb_init_done; /* set when buffers are initialised */ + unsigned long flags; /* for saving interrupt flags */ + struct t_buf *buf; /* the buffer we're working on */ + struct t_rec *rec; /* next record to fill out */ + + + if(!atomic_read(&tb_init_done)) return -1; + + buf = t_bufs[smp_processor_id()]; + rec = buf->head_ptr; + + spin_lock_irqsave(&buf->lock, flags); + /* interrupts _disabled locally_ during the following code */ + + rdtscll(rec->cycles); + rec->event = event; + rec->d1 = d1; + rec->d2 = d2; + rec->d3 = d3; + rec->d4 = d4; + rec->d5 = d5; + + wmb(); /* above must be visible before reader sees index updated */ + + if( likely( buf->head_ptr < ( buf->vdata + buf->size - 1) ) ) { + buf->head_ptr++; + buf->head++; + } else { + buf->head = 0; + buf->head_ptr = buf->vdata; + } + + spin_unlock_irqrestore(&buf->lock, flags); + /* Interrupts now _re-enabled locally_ */ + + return 0; +} + + +#endif /* #ifdef __KERNEL__ */ + + +/****************************************************************************** + * Macros + *****************************************************************************/ + +/* How much space is allowed for a single trace buffer, including data and + * metadata (and maybe some waste). + */ +#define TB_SIZE PAGE_SIZE + +#ifdef __KERNEL__ + +/* avoids troubling the caller with casting their arguments to a trace macro */ +#define trace_do_casts(e,d1,d2,d3,d4,d5) \ + trace(e, \ + (unsigned long)d1, \ + (unsigned long)d2, \ + (unsigned long)d3, \ + (unsigned long)d4, \ + (unsigned long)d5) + +/* convenience macros for calling the trace function */ +#define TRACE_0D(event) trace_do_casts(event,0, 0, 0, 0, 0 ) +#define TRACE_1D(event,d) trace_do_casts(event,d, 0, 0, 0, 0 ) +#define TRACE_2D(event,d1,d2) trace_do_casts(event,d1,d2,0, 0, 0 ) +#define TRACE_3D(event,d1,d2,d3) trace_do_casts(event,d1,d2,d3,0, 0 ) +#define TRACE_4D(event,d1,d2,d3,d4) trace_do_casts(event,d1,d2,d3,d4,0 ) +#define TRACE_5D(event,d1,d2,d3,d4,d5) trace_do_casts(event,d1,d2,d3,d4,d5) + +#endif /* #ifdef __KERNEL__ */ + +#endif /* #ifndef __XENO_TRACE_H__ */ + +#else /* #ifdef TRACE_BUFFER */ + +/* define out macros so that they can be left in code when tracing is disabled */ +#define TRACE_0D(event) ((void)0) +#define TRACE_1D(event,d) ((void)0) +#define TRACE_2D(event,d1,d2) ((void)0) +#define TRACE_3D(event,d1,d2,d3) ((void)0) +#define TRACE_4D(event,d1,d2,d3,d4) ((void)0) +#define TRACE_5D(event,d1,d2,d3,d4,d5) ((void)0) + +#endif /* #ifdef TRACE_BUFFER */ -- 2.30.2